
use serde::{Deserialize, Serialize, Serializer, Deserializer, de::Visitor};
use serde::de;

use std::fmt;
use std::cmp::{Eq, PartialEq};
use std::hash::Hash;

#[derive(Debug, Clone, Eq, PartialEq, Hash)]
pub enum TaskType {
    AddPiece,
    PreCommit1,
    PreCommit2,
    Commit1,
    Commit2,
    Finalize,
    Fetch,
    Unseal,
}

impl TaskType {

    pub fn from_str(value: &str) -> anyhow::Result<Self> {
        match value {
            "seal/v0/addpiece" | "AP" => Ok(TaskType::AddPiece),
            "seal/v0/precommit/1" | "PC1" => Ok(TaskType::PreCommit1),
            "seal/v0/precommit/2" | "PC2" => Ok(TaskType::PreCommit2),
            "seal/v0/commit/1" | "C1" => Ok(TaskType::Commit1),
            "seal/v0/commit/2" | "C2" => Ok(TaskType::Commit2),
            "seal/v0/finalize" | "FIN" => Ok(TaskType::Finalize),
            "seal/v0/fetch" | "GET" => Ok(TaskType::Fetch),
            "seal/v0/unseal" | "UNS" => Ok(TaskType::Unseal),
            _ => Err(anyhow::Error::msg(format!("unknown task type string {}.", value)))
        }
    }

    pub fn short(&self) -> &str {
        match self {
            &Self::AddPiece => "AP",
            &Self::PreCommit1 => "PC1",
            &Self::PreCommit2 => "PC2",
            &Self::Commit1 => "C1",
            &Self::Commit2 => "C2",
            &Self::Finalize => "FIN",
            &Self::Fetch => "GET",
            &Self::Unseal => "UNS",
        }
    }

    pub fn name(&self) -> &str {
        match self {
            &Self::AddPiece => "seal/v0/addpiece",
            &Self::PreCommit1 => "seal/v0/precommit/1",
            &Self::PreCommit2 => "seal/v0/precommit/2",
            &Self::Commit1 => "seal/v0/commit/1",
            &Self::Commit2 => "seal/v0/commit/2",
            &Self::Finalize => "seal/v0/finalize",
            &Self::Fetch => "seal/v0/fetch",
            &Self::Unseal => "seal/v0/unseal",
        }
    }
}

impl Serialize for TaskType
{
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
        where
            S: Serializer,
    {
        serializer.serialize_str(self.name())
    }
}

impl <'de> Deserialize<'de> for TaskType {
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> where D: Deserializer<'de> {
        deserializer.deserialize_str(TaskTypeVisitor::new())
    }
}

struct TaskTypeVisitor;

impl TaskTypeVisitor {
    fn new() -> TaskTypeVisitor {
        TaskTypeVisitor{}
    }
}

impl<'de> Visitor<'de> for TaskTypeVisitor {
    type Value = TaskType;

    fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
        formatter.write_str("a string")
    }

    fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
        where
            E: de::Error,
    {
        let ret = Self::Value::from_str(value);
        match ret {
            Ok(t) => Ok(t),
            Err(e) => Err(E::custom(e))
        }
    }
}